﻿using WebSocketServer.Models;
using JmShared;
using PushSharp;
using PushSharp.Android;
using PushSharp.Core;
using PushSharp.WindowsPhone;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using INotification = PushSharp.Core.INotification;
using WebSocketServer.Utilities;
using PushSharp.Windows;
using Newtonsoft.Json;
using System.Net;
using System.Web;

namespace WebSocketServer.Managers
{
    public class PushMessageManager
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        //public int _sendtPushMessages;
        private readonly System.Timers.Timer _tilePushTimer;
        private PushBroker _pushBroker = new PushBroker();
        public event EventHandler<WriteLogEventArgs> WriteLogEvent;
        public PushMessageManager()
        {
            this._tilePushTimer = new System.Timers.Timer();
            this._tilePushTimer.Interval = 30000;
            this._tilePushTimer.Elapsed += new ElapsedEventHandler(TilePushTimer_Elapsed);
            this._tilePushTimer.AutoReset = false;

            _pushBroker.OnNotificationSent += NotificationSent;
            _pushBroker.OnChannelException += ChannelException;
            _pushBroker.OnServiceException += ServiceException;
            _pushBroker.OnNotificationFailed += NotificationFailed;
            _pushBroker.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired;
            _pushBroker.OnDeviceSubscriptionChanged += DeviceSubscriptionChanged;
            _pushBroker.OnChannelCreated += ChannelCreated;
            _pushBroker.OnChannelDestroyed += ChannelDestroyed;


            string windowsPushPackageName = System.Configuration.ConfigurationManager.AppSettings["WindowsPushPackageName"];
            string windowsPushPackageSID = System.Configuration.ConfigurationManager.AppSettings["WindowsPushPackageSID"];
            string windowsPushPackageClientSecret = System.Configuration.ConfigurationManager.AppSettings["WindowsPushClientSecret"];

            string androidPushAuthToken = System.Configuration.ConfigurationManager.AppSettings["AndroidPushAuthToken"];

            var windowsChannelSettings = new WindowsPushChannelSettings(windowsPushPackageName, windowsPushPackageSID, windowsPushPackageClientSecret);

            //_pushBroker.RegisterWindowsService(windowsChannelSettings);
            _pushBroker.RegisterService<WindowsToastNotification>(new WindowsPushService(windowsChannelSettings));
            _pushBroker.RegisterService<WindowsTileNotification>(new WindowsPushService(windowsChannelSettings));
            _pushBroker.RegisterService<WindowsBadgeNumericNotification>(new WindowsPushService(windowsChannelSettings));
            _pushBroker.RegisterGcmService(new GcmPushChannelSettings(androidPushAuthToken));
        }
        public void StopTilePushTimer()
        {
            this._tilePushTimer.Stop();
        }
        public void StartTilePushTimer()
        {
            this._tilePushTimer.Start();
        }
        private void DeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, INotification notification)
        {
            //Currently this event will only ever happen for Android GCM
            WriteLog("Device Registration Changed:  Old-> " + oldSubscriptionId + "  New-> " + newSubscriptionId + " -> " + notification);
        }

        private void NotificationSent(object sender, INotification notification)
        {
            // WriteLog("Sent: " + sender + " -> " + notification);
        }

        private void NotificationFailed(object sender, INotification notification, Exception notificationFailureException)
        {
            WriteLog("Notification Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification);
            log.Error("Notification Failure: ", notificationFailureException);
        }

        private void ChannelException(object sender, IPushChannel channel, Exception exception)
        {
            WriteLog("Channel Exception: " + sender + " -> " + exception);
            log.Error("Channel Exception: ", exception);
        }

        private void ServiceException(object sender, Exception exception)
        {
            WriteLog("Service Exception: " + sender + " -> " + exception);
            log.Error("Service Exception: ", exception);
        }

        private void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification)
        {
            WriteLog("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId);
        }

        private void ChannelDestroyed(object sender)
        {
            WriteLog("Channel Destroyed for: " + sender);
        }

        private void ChannelCreated(object sender, IPushChannel pushChannel)
        {
            WriteLog("Channel Created for: " + sender);
        }

        private void TilePushTimer_Elapsed(object sender, ElapsedEventArgs e)
        {

            Timer timer = sender as Timer;
            timer.Enabled = false;
            try
            {
                if (DB.DbManager == null)
                    return;
                var onlineClients = Clients.ConnectedClients.Values.Where(x => x.DeviceType == DeviceType.WindowsPhone
                    && x.ReceivedMessageCount > x.DeliveredMessageCount
                    && x.ReceivedMessageCount < 150
                    && !String.IsNullOrEmpty(x.PushUri) 
                    && x.IsActive.GetValueOrDefault() == false);
                foreach (ChatClient client in onlineClients)
                {
                        this.SendTilePush(client);
                }
                //foreach (ConcurrentDictionary<string, ChatClient> dic in Clients.OfflineClientsByNameAndID.Values)
                //{
                List<OfflineClient> offlinePushTargets = DB.DbManager.ReadOfflineWPTilePushTargets();
                if (offlinePushTargets.Count > 0)
                {
                    // foreach (ChatClient client in Clients.OfflineClientsByID.Values.Where(x => x.DeviceType != DeviceType.PC && !String.IsNullOrEmpty(x.PushUri) && x.UnreadMessageCount > 0))
                    bool sendtTilePush = false;
                    foreach (var client in offlinePushTargets)
                    {
                        if (client.ReceivedMessageCount < 150)
                        {
                            sendtTilePush = this.SendTilePush(client);
                        }
                    }
                    if (sendtTilePush)
                        DB.DbManager.StoreDeliveredMessageCount(offlinePushTargets);
                }
                //}
            }
            finally
            {
                timer.Enabled = true;
            }
        }
        private void WriteLog(string str)
        {
            if (WriteLogEvent != null)
                WriteLogEvent(this, new WriteLogEventArgs(str));
        }
        public void StopAllServices()
        {

            _pushBroker.StopAllServices();
        }
        public void SendToastPush(string pushUri, string senderName, ChatMessage msg)
        {
            try
            {
                if (String.IsNullOrEmpty(pushUri))
                    return;

                var notification = new WindowsToastNotification()
                .ForChannelUri(pushUri)
                .AsToastText02(senderName, msg.Message);

                notification.Launch = JsonConvert.SerializeObject(msg);

                _pushBroker.QueueNotification(notification);
                // Get the Uri that the Microsoft Push Notification Service returns to the Push Client when creating a notification channel. 
                // Normally, a web service would listen for Uri's coming from the web client and maintain a list of Uri's to send 
                // notifications out to. 

                //this._pushBroker.QueueNotification(new WindowsPhoneToastNotification()
                //    .ForEndpointUri(new Uri(pushUri))
                //    .ForOSVersion(WindowsPhoneDeviceOSVersion.Eight)
                //    .WithBatchingInterval(BatchingInterval.Immediate)
                //    .WithNavigatePath(string.Format("/{0}.xaml?NavigatedFrom={1}", "MainPage", isPm ? "pmtoast" : "grouptoast"))
                //    .WithText1(senderName)
                //    .WithText2(message));

                //HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);

                //// We will create a HTTPWebRequest that posts the raw notification to the Microsoft Push Notification Service. 
                //// HTTP POST is the only allowed method to send the notification. 
                //sendNotificationRequest.Method = "POST";

                //// Create the toast message.
                //string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                //"<wp:Notification xmlns:wp=\"WPNotification\">" +
                //   "<wp:Toast>" +
                //        "<wp:Text1>" + WebUtility.HtmlEncode(sender) + "</wp:Text1>" +
                //        "<wp:Text2>" + WebUtility.HtmlEncode(message) + "</wp:Text2>" +
                //    //"<wp:Param>/MainPage.xaml?NavigatedFrom=Toast Notification</wp:Param>" +
                //                                "<wp:Param>/MainPage.xaml</wp:Param>" +
                //   "</wp:Toast> " +
                //"</wp:Notification>";

                //// Set the notification payload to send.
                //byte[] notificationMessage = Encoding.Default.GetBytes(toastMessage);
                ////// Sets the web request content length. 
                //sendNotificationRequest.ContentLength = notificationMessage.Length;
                //sendNotificationRequest.ContentType = "text/xml";
                //sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
                //sendNotificationRequest.Headers.Add("X-NotificationClass", "2");

                //using (Stream requestStream = sendNotificationRequest.GetRequestStream())
                //{
                //    requestStream.Write(notificationMessage, 0, notificationMessage.Length);
                //}

                //// Send the notification and get the response. 
                //HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
                //string notificationStatus = response.Headers["X-NotificationStatus"];
                //string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
                //string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
                //this.WriteLog(String.Format("Sendt toast push to {0}: {1}, {2}, {3}", recipient.User.UserName, notificationStatus, notificationChannelStatus, deviceConnectionStatus));

                //this.Invoke((Action)(() => this.lblSendtPush1.Text = "Daily push: " + Interlocked.Increment(ref this._sendtPushMessages)));
                //// Display the response from the Microsoft Push Notification Service.   
                //// Normally, error handling code would be here.  In the real world, because data connections are not always available, 
                //// notifications may need to be throttled back if the device cannot be reached. 
                ////this.Invoke((Action)(() => this.txtBoxLog.AppendText(notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus)));
            }
            catch (Exception ex)
            {

                log.Error("Error in push: ", ex);
                // this.Invoke((Action)(() => this.txtBoxLog.AppendText("Exception caught sending update: " + ex.Message + "\r\n")));
                WriteLog("Exception caught sending toast push\r\n");
            }
        }


        public void SendTilePush(ChatClient recipient)
        {
            try
            {
                if (String.IsNullOrEmpty(recipient.PushUri))
                    return;
                if (recipient.ReceivedMessageCount == 0)
                    return;

                foreach (KeyValuePair<string, UnreadUserMessages> kvp in recipient.UnreadMessageStats.OrderByDescending(x => x.Value.LastReceivedMessageTime))
                {
                    DateTimeOffset receiverTime = new DateTimeOffset(kvp.Value.LastReceivedMessageTime).ToOffset(TimeSpan.FromHours(recipient.TimeZoneOffset.GetValueOrDefault(0)));
                    string senderName = !string.IsNullOrEmpty(kvp.Value.SenderNickname) ? kvp.Value.SenderNickname : kvp.Value.SenderUsername;
                    string lastMsgTime = receiverTime.ToString("HH:mm");
                    int msgCount = kvp.Value.UnreadMessageCount;
                    string tag = HashLib.HashFactory.Checksum.CreateAdler32().ComputeString(senderName).ToString();
                    _pushBroker.QueueNotification(new WindowsTileNotification()
                        .ForChannelUri(recipient.PushUri)
                        .WithTileSquare150x150IconWithBadge("Assets/SmallLogo.scale-240.png")
                        .WithTileWide310x150IconWithBadgeAndText(senderName, string.Format("Unread messages: {0}", msgCount), string.Format("Last message at: {0}", lastMsgTime), "Assets/SmallLogo.scale-240.png")
                        .WithTag(tag)
                        .WithVersion(3));
                }


                _pushBroker.QueueNotification(new WindowsBadgeNumericNotification()
                {
                    ChannelUri = recipient.PushUri,
                    BadgeNumber = recipient.ReceivedMessageCount,
                });
                //this._pushBroker.QueueNotification(new WindowsPhoneIconicTileNotification()
                //    .ForEndpointUri(new Uri(recipient.PushUri))
                //    .ForOSVersion(WindowsPhoneDeviceOSVersion.Eight)
                //    .WithBatchingInterval(BatchingInterval.Immediate)
                //    //.WithSmallIconImage("Assets/1xfeather-70x110px.png")
                //    //.WithIconImage("Assets/1xfeather-130x202px.png")
                //    .WithCount(recipient.ReceivedMessageCount)
                //    .WithBackgroundColor("#00ffffff"));

                recipient.DeliveredMessageCount = recipient.ReceivedMessageCount;

            }
            catch (Exception ex)
            {
                log.Error("Error in push: ", ex);
                WriteLog("Exception caught sending tile push\r\n");
            }
        }
        public bool SendTilePush(OfflineClient recipient)
        {
            try
            {
                if (String.IsNullOrEmpty(recipient.PushUri))
                    return false;
                if (recipient.ReceivedMessageCount == 0)
                    return false;

                //KeyValuePair<string, KeyValuePair<int, DateTime>> kvp = recipient.UnreadMessageDic.OrderByDescending(x => x.Value.Value).FirstOrDefault();
                //DateTimeOffset receiverTime = new DateTimeOffset(kvp.Value.Value).ToOffset(TimeSpan.FromHours(recipient.TimeZoneOffset));


                foreach (KeyValuePair<string, UnreadUserMessages> kvp in recipient.UnreadMessageStats.OrderByDescending(x => x.Value.LastReceivedMessageTime))
                {
                    DateTimeOffset receiverTime = new DateTimeOffset(kvp.Value.LastReceivedMessageTime).ToOffset(TimeSpan.FromHours(recipient.TimeZoneOffset.GetValueOrDefault(0)));
                    string senderName = !string.IsNullOrEmpty(kvp.Value.SenderNickname) ? kvp.Value.SenderNickname : kvp.Value.SenderUsername;
                    string lastMsgTime = receiverTime.ToString("HH:mm");
                    int msgCount = kvp.Value.UnreadMessageCount;
                    string tag = HashLib.HashFactory.Checksum.CreateAdler32().ComputeString(senderName).ToString();
                    _pushBroker.QueueNotification(new WindowsTileNotification()
                        .ForChannelUri(recipient.PushUri)
                        .WithTileSquare150x150IconWithBadge("Assets/SmallLogo.scale-240.png")
                        .WithTileWide310x150IconWithBadgeAndText(senderName, string.Format("Unread messages: {0}", msgCount), string.Format("Last message at: {0}", lastMsgTime), "Assets/SmallLogo.scale-240.png")
                        .WithTag(tag)
                        .WithVersion(3));
                }
                _pushBroker.QueueNotification(new WindowsBadgeNumericNotification()
                {
                    ChannelUri = recipient.PushUri,
                    BadgeNumber = recipient.ReceivedMessageCount,
                });
                //this._pushBroker.QueueNotification(new WindowsPhoneIconicTileNotification()
                //    .ForEndpointUri(new Uri(recipient.PushUri))
                //    .ForOSVersion(WindowsPhoneDeviceOSVersion.Eight)
                //    .WithBatchingInterval(BatchingInterval.Immediate)
                //    //.WithSmallIconImage("Assets/1xfeather-70x110px.png")
                //    //.WithIconImage("Assets/1xfeather-130x202px.png")
                //    .WithCount(recipient.ReceivedMessageCount)// <= 99 ? recipient.ReceivedMessageCount : 99)
                //    .WithBackgroundColor("#00ffffff"));

                recipient.DeliveredMessageCount = recipient.ReceivedMessageCount;
                return true;

            }
            catch (Exception ex)
            {
                log.Error("Error in push: ", ex);
                WriteLog("Exception caught sending tile push\r\n");
                return false;
            }
        }
    }


    public class WriteLogEventArgs : EventArgs
    {
        public string Message { get; set; }
        public WriteLogEventArgs(string str)
        {
            this.Message = str;
        }
    }
}
